/**
* File : math3D.h
* Description : Class for fixed point operations, based on math-sll.h (by Chui)
*				  
* Author(s) : Gaijin
* Date of creation : 08/01/2006
* Modification(s) :
*/

//-------------------------------------------------------------------- INCLUDES

#include "math-sll.h"
//---------------------------------------------------------------------- CONSTS

//---------------------------------------------------------------------- MACROS

//----------------------------------------------------------------------- TYPES

//--------------------------------------------------------------------- CLASSES

/*TODO
      -improve *= operator to use mul2, mul4...
      -namespace to use with cos, sin, tan atan??? (Class Methods)
      
*/

class Sll
{

public:
    
    /*Constructors*/   
    inline Sll()
    {
      sValue=SLL_CONST_0;
    };
    
    inline Sll(int a)
    {
      sValue =int2sll(a);
    };

    inline Sll(long int a)
    {
      sValue =int2sll((int)a);
    };

    inline Sll(const Sll& a)
    {
      sValue = a.sValue;
    };

    inline Sll(double dbl)
    {
      sValue = dbl2sll(dbl);
    };

    inline Sll(float a)
    {
      sll((double) a);
    };
  
    inline Sll(sll a)
    {
      sValue = a;    
    };
    
    /*Operator overloading*/

    inline operator int() const
    {
           return sll2int(sValue);
    };

    inline operator double() const
    {
           return sll2dbl(sValue);
    };

    
    inline Sll& operator+=(const Sll& b) 
    {
       sValue = slladd(sValue, b.sValue);
       return *this;
    }

    inline Sll& operator+=(const int b) 
    {
       sValue = slladd(sValue, int2sll(b));
       return *this;
    }

    inline Sll& operator+=(const float b) 
    {
       sValue = slladd(sValue, dbl2sll((double)b));
       return *this;
    }

    inline Sll& operator+=(const double b) 
    {
       sValue = slladd(sValue, dbl2sll(b));
       return *this;
    }
  
    inline Sll& operator-=(const Sll& b) 
    {
       sValue = sllsub(sValue, b.sValue);
       return *this;
    }

    inline Sll& operator-=(const int b) 
    {
       sValue = sllsub(sValue, int2sll(b));
       return *this;
    }

    inline Sll& operator-=(const float b) 
    {
       sValue = sllsub(sValue, dbl2sll((double)b));
       return *this;
    }

    inline Sll& operator-=(const double b) 
    {
       sValue = sllsub(sValue, dbl2sll(b));
       return *this;
    }


    inline Sll& operator*=(const Sll& b) 
    {
       sValue = sllmul(sValue, b.sValue);
       return *this;
    }

    inline Sll& operator*=(const int b) 
    {
       sValue = sllmul(sValue, int2sll(b));
       return *this;
    }

    inline Sll& operator*=(const float b) 
    {
       sValue = sllmul(sValue, dbl2sll((double)b));
       return *this;
    }

    inline Sll& operator*=(const double b) 
    {
       sValue = sllmul(sValue, dbl2sll(b));
       return *this;
    }


    inline Sll& operator/=(const Sll& b) 
    {
       if (b.sValue==2)
          sValue = slldiv2(sValue);
       else if (b.sValue==4)
          sValue = slldiv4(sValue);
          else 
             sValue = slldiv(sValue, b.sValue);
       return *this;
    }

    inline Sll& operator/=(const double b) 
    {
       if (b==2)
          sValue = slldiv2(sValue);
       else if (b==4)
          sValue = slldiv4(sValue);
          else 
             sValue = slldiv(sValue, dbl2sll(b));
       return *this;
    }

    inline Sll& operator/=(const float b)
    {
       return (operator/=((double)b));
    }

    inline Sll& operator/=(const int b)
    {
       if (b==2)
          sValue = slldiv2(sValue);
       else if (b==4)
          sValue = slldiv4(sValue);
          else 
             sValue = slldiv(sValue, int2sll(b));
       return *this;

    }


    /*Affectation*/
    inline Sll& operator=(const Sll& right)
    {
      sValue=right.sValue;
      return *this;
    };
    
    inline Sll& operator=(const int right)
    {
      sValue=int2sll(right);
      return *this;
    };
    
    inline Sll& operator=(const float right)
    {
      sValue=dbl2sll((double)right);
      return *this;
    };
    
    inline Sll& operator=(const double right)
    {
      sValue=dbl2sll(right);
      return *this;
    };


    /*Somme*/
    inline friend Sll operator+(const Sll& left, const Sll& right)
    {
       return Sll(slladd(left.sValue,right.sValue));
    };

    inline friend Sll operator+(const Sll& left, const int right)
    {
       return Sll(slladd(left.sValue,int2sll(right)));
    };

    inline friend Sll operator+(const Sll& left, const long right)
    {
       return Sll(slladd(left.sValue,int2sll((int)right)));
    };

    inline friend Sll operator+(const Sll& left, const float right)
    {
       return Sll(slladd(left.sValue,dbl2sll((double)right)));
    };

    inline friend Sll operator+(const Sll& left, const double right)
    {
       return Sll(slladd(left.sValue,dbl2sll(right)));
    };

/*************/

    inline friend Sll operator+(const int left, const Sll& right)
    {
       return Sll(slladd(right.sValue,int2sll(left)));
    };

    inline friend Sll operator+(const long left, const Sll& right)
    {
       return Sll(slladd(right.sValue,int2sll((int)left)));
    };

    inline friend Sll operator+(const float left, const Sll& right)
    {
       return Sll(slladd(right.sValue,dbl2sll((double)left)));
    };

    inline friend Sll operator+(const double left, const Sll& right)
    {
       return Sll(slladd(right.sValue,dbl2sll(left)));
    };

    /*Substraction*/
    inline friend Sll operator-(const Sll& left, const Sll& right)
    {
       return Sll(sllsub(left.sValue,right.sValue));
    };

    inline friend Sll operator-(const Sll& left, const int right)
    {
       return Sll(sllsub(left.sValue,int2sll(right)));
    };

    inline friend Sll operator-(const Sll& left, const float right)
    {
       return Sll(sllsub(left.sValue,dbl2sll((double)right)));
    };

    inline friend Sll operator-(const Sll& left,const double right)
    {
       return Sll(sllsub(left.sValue,dbl2sll(right)));
    };
/*****************/

    inline friend Sll operator-(const int left, const Sll& right)
    {
       return Sll(sllsub(int2sll(left),right.sValue));
    };

    inline friend Sll operator-(const float left, const Sll& right)
    {
       return Sll(sllsub(dbl2sll((double)left),right.sValue));
    };

    inline friend Sll operator-(const double left, const Sll& right)
    {
       return Sll(sllsub(dbl2sll(left),right.sValue));
    };

    inline friend Sll operator-(const Sll& right)
    {
       return Sll(-1 * right);      
    };


    /*Multiplication*/
    inline friend Sll operator*(const Sll& left, const Sll& right)
    {
       return Sll(sllmul(left.sValue,right.sValue));
    };

    inline friend Sll operator*(const Sll& left, const long right)
    {
       return (operator*(left, (int)right));         
    };

    /*We should add the case sllmul2n, make a case with most important values?*/
    inline friend Sll operator*(const Sll& left, const int right)
    {
       switch(right){
          case 2:   return Sll(sllmul2(left.sValue));break;
          case 4:   return Sll(sllmul4(left.sValue));break;
          case 8:   return Sll(sllmul2n(left.sValue,3));break;
          case 16:  return Sll(sllmul2n(left.sValue,4));break;
          case 32:  return Sll(sllmul2n(left.sValue,5));break;
          case 64:  return Sll(sllmul2n(left.sValue,6));break;
          case 128: return Sll(sllmul2n(left.sValue,7));break;
          case 256: return Sll(sllmul2n(left.sValue,8));break;
          case 1024:return Sll(sllmul2n(left.sValue,9));break;
          default:
                return Sll(sllmul(left.sValue,int2sll(right))); break;
       }
    };

    inline friend Sll operator*(const Sll& left, const float right)
    {
       return Sll(sllmul(left.sValue,dbl2sll((double)right)));
    };

    inline friend Sll operator*(const Sll& left, const double right)
    {
       return Sll(sllmul(left.sValue,dbl2sll(right)));
    };
/***************/

    inline friend Sll operator*(const long left, const Sll& right)
    {
       return ((int)left,right);
    };

    /*We should add the case sllmul2n, make a case with most important values?*/
    inline friend Sll operator*(const int left, const Sll& right)
    {
       switch(left){
          case 2:   return Sll(sllmul2(right.sValue));break;
          case 4:   return Sll(sllmul4(right.sValue));break;
          case 8:   return Sll(sllmul2n(right.sValue,3));break;
          case 16:  return Sll(sllmul2n(right.sValue,4));break;
          case 32:  return Sll(sllmul2n(right.sValue,5));break;
          case 64:  return Sll(sllmul2n(right.sValue,6));break;
          case 128: return Sll(sllmul2n(right.sValue,7));break;
          case 256: return Sll(sllmul2n(right.sValue,8));break;
          case 1024:return Sll(sllmul2n(right.sValue,9));break;
          default:
                return Sll(sllmul(int2sll(left),right.sValue)); break;
       }
    };

    inline friend Sll operator*(const float left, const Sll& right)
    {
       return Sll(sllmul(dbl2sll((double)left),right.sValue));
    };

    inline friend Sll operator*(const double left, const Sll& right)
    {
       return Sll(sllmul(dbl2sll(left),right.sValue));
    };

    /*Division*/
    inline friend Sll operator/(const Sll& left, const Sll& right)
    {
       return Sll(slldiv(left.sValue,right.sValue));
    };

    inline friend Sll operator/(const Sll& left, const int right)
    {
       switch(right){
          case 2:   return Sll(slldiv2(left.sValue));break;
          case 4:   return Sll(slldiv4(left.sValue));break;
          case 8:   return Sll(slldiv2n(left.sValue,3));break;
          case 16:  return Sll(slldiv2n(left.sValue,4));break;
          case 32:  return Sll(slldiv2n(left.sValue,5));break;
          case 64:  return Sll(slldiv2n(left.sValue,6));break;
          case 128: return Sll(slldiv2n(left.sValue,7));break;
          case 256: return Sll(slldiv2n(left.sValue,8));break;
          case 1024:return Sll(slldiv2n(left.sValue,9));break;
          default:
                return Sll(slldiv(left.sValue,int2sll(right))); break;
       }
    };

    inline friend Sll operator/(const Sll& left, const float right)
    {
       return Sll(slldiv(left.sValue,dbl2sll((double)right)));
    };

    inline friend Sll operator/(const Sll& left, const double right)
    {
       return Sll(slldiv(left.sValue,dbl2sll(right)));
    };
/*********/

    inline friend Sll operator/(const int left, const Sll& right)
    {
       if (left == 1)
          return Sll(sllinv(right.sValue));
       else
          return Sll(slldiv(int2sll(left),right.sValue));
    };

    inline friend Sll operator/(const float left,const Sll& right)
    {
       if (left == 1.0f )
          return Sll(sllinv(right.sValue));
       else
          return Sll(slldiv(dbl2sll((double)left),right.sValue));
    };

    inline friend Sll operator/(const double left, const Sll& right)
    {
       if (left == 1)
          return Sll(sllinv(right.sValue));
       else
          return Sll(slldiv(dbl2sll(left),right.sValue));
    };

    
    /*Comparation*/       
    inline friend bool operator<=(const Sll& left, const Sll& right)
    {
       return (left.sValue <= right.sValue);
    };
    
    inline friend bool operator<=(const Sll& left, const int right)
    {
       return (left.sValue <= int2sll(right));
    };

    inline friend bool operator<=(const Sll& left, const float right)
    {
       return (left.sValue <= dbl2sll((double)right));
    };

    inline friend bool operator<=(const Sll& left, const double right)
    {
       return (left.sValue <= dbl2sll(right));
    };

/********/
    inline friend bool operator<=(const int left, const Sll& right)
    {
       return (int2sll(left) <= right.sValue);
    };

    inline friend bool operator<=(const float left, const Sll& right)
    {
       return (dbl2sll((double)left) <= right.sValue);
    };

    inline friend bool operator<=(const double left, const Sll& right)
    {
       return (dbl2sll(left) <= right.sValue);
    };

/*********************/
    inline friend bool operator>=(const Sll& left, const Sll& right)
    {
       return (left.sValue >= right.sValue);
    };
    
    inline friend bool operator>=(const Sll& left, const int right)
    {
       return (left.sValue >= int2sll(right));
    };

    inline friend bool operator>=(const Sll& left, const float right)
    {
       return (left.sValue >= dbl2sll((double)right));
    };

    inline friend bool operator>=(const Sll& left, const double right)
    {
       return (left.sValue >= dbl2sll(right));
    };

/********/
    inline friend bool operator>=(const int left, const Sll& right)
    {
       return (int2sll(left) >= right.sValue);
    };

    inline friend bool operator>=(const float left, const Sll& right)
    {
       return (dbl2sll((double)left) >= right.sValue);
    };

    inline friend bool operator>=(const double left, const Sll& right)
    {
       return (dbl2sll(left) >= right.sValue);
    };

/******************/

    inline friend bool operator<(const Sll& left, const Sll& right)
    {
       return (left.sValue < right.sValue);
    };

    inline friend bool operator<(const Sll& left, const int right)
    {
       return (left.sValue < int2sll(right));
    };

    inline friend bool operator<(const Sll& left, const float right)
    {
       return (left.sValue < dbl2sll((double)right));
    };

    inline friend bool operator<(const Sll& left, const double right)
    {
       return (left.sValue < dbl2sll(right));
    };

/********/
    inline friend bool operator<(const int left, const Sll& right)
    {
       return (int2sll(left) < right.sValue);
    };

    inline friend bool operator<(const float left, const Sll& right)
    {
       return (dbl2sll((double)left) < right.sValue);
    };

    inline friend bool operator<(const double left, const Sll& right)
    {
       return (dbl2sll(left) < right.sValue);
    };

/*********************/
    inline friend bool operator>(const Sll& left, const Sll& right)
    {
       return (left.sValue > right.sValue);
    };

    inline friend bool operator>(const Sll& left, const int right)
    {
       return (left.sValue > int2sll(right));
    };

    inline friend bool operator>(const Sll& left, const float right)
    {
       return (left.sValue > dbl2sll((double)right));
    };

    inline friend bool operator>(const Sll& left, const double right)
    {
       return (left.sValue > dbl2sll(right));
    };

/********/
    inline friend bool operator>(const int left, const Sll& right)
    {
       return (int2sll(left) > right.sValue);
    };

    inline friend bool operator>(const float left, const Sll& right)
    {
       return (dbl2sll((double)left) > right.sValue);
    };

    inline friend bool operator>(const double left, const Sll& right)
    {
       return (dbl2sll(left) > right.sValue);
    };
/*********************/
    inline friend bool operator==(const Sll& left, const Sll& right)
    {
       return (left.sValue == right.sValue);
    };

    inline friend bool operator==(const Sll& left, const int right)
    {
       return (left.sValue == int2sll(right));
    };

    inline friend bool operator==(const Sll& left, const float right)
    {
       return (left.sValue == dbl2sll((double)right));
    };

    inline friend bool operator==(const Sll& left, const double right)
    {
       return (left.sValue == dbl2sll(right));
    };

/********/
    inline friend bool operator==(const int left, const Sll& right)
    {
       return (int2sll(left) == right.sValue);
    };

    inline friend bool operator==(const float left, const Sll& right)
    {
       return (dbl2sll((double)left) == right.sValue);
    };

    inline friend bool operator==(const double left, const Sll& right)
    {
       return (dbl2sll(left) == right.sValue);
    };

/********************/
/*    inline friend double operator<<(string a, const Sll& x)
    {
       return ""+(sll2dbl(x.sValue));
    };

*/
    /*Trigonometric methods, they're class methods, see TODO*/
    /*Sinus*/
    inline static Sll sin(const Sll& x)
    {
       return Sll(sllsin(x.sValue));
    };

    inline static Sll sin(const int x)
    {
       return Sll(sllsin(int2sll(x)));
    };

    inline static Sll sin(const float x)
    {
       return Sll(sllsin(dbl2sll((double)x)));
    };

    inline static Sll sin(const double x)
    {
       return Sll(sllsin(dbl2sll(x)));
    };
    
    /*Cosinus*/
    inline static Sll cos(const Sll& x)
    {
       return Sll(sllcos(x.sValue));
    };

    inline static Sll cos(const int x)
    {
       return Sll(sllcos(int2sll(x)));
    };

    inline static Sll cos(const float x)
    {
       return Sll(sllcos(dbl2sll((double)x)));
    };

    inline static Sll cos(const double x)
    {
       return Sll(sllcos(dbl2sll(x)));
    };
    
    /*Tangente*/
    inline static Sll tan(const Sll& x)
    {
       return Sll(slltan(x.sValue));
    };

    inline static Sll tan(const int x)
    {
       return Sll(slltan(int2sll(x)));
    };

    inline static Sll tan(const float x)
    {
       return Sll(slltan(dbl2sll((double)x)));
    };

    inline static Sll tan(const double x)
    {
       return Sll(slltan(dbl2sll(x)));
    };

    /*Cotangent*/
    inline static Sll cot(const Sll& x)
    {
       return Sll::inv(Sll::tan(x));
    };

    inline static Sll cot(const int x)
    {
       return Sll::inv(Sll::tan(x));
    };

    inline static Sll cot(const float x)
    {
       return Sll::inv(Sll::tan(x));
    };

    inline static Sll cot(const double x)
    {
       return Sll::inv(Sll::tan(x));
    };
    
    /*Arctangente*/
    inline static Sll atan(const Sll& x)
    {
       return Sll(sllatan(x.sValue));
    };

    inline static Sll atan(const int x)
    {
       return Sll(sllatan(int2sll(x)));
    };

    inline static Sll atan(const float x)
    {
       return Sll(sllatan(dbl2sll((double)x)));
    };

    inline static Sll atan(const double x)
    {
       return Sll(sllatan(dbl2sll(x)));
    };


    inline static Sll inv(const Sll& x)
    {
          Sll a;
          a.sValue = sllinv(x.sValue);
          return a;
    }

    inline static Sll inv(const int x)
    {
          Sll a;
          a.sValue = sllinv(int2sll(x));
          return a;
    }


    inline static Sll inv(const double x)
    {
          Sll a;
          a.sValue = sllinv(dbl2sll(x));
          return a;
    }

    inline static Sll inv(const float x)
    {
          Sll a;
          a.sValue = sllinv(dbl2sll(x));
          return a;
    }


    inline static Sll sqrt(const Sll& x)
    {
           return Sll(sllsqrt(x.sValue));
    };

    inline static Sll sqrt(const int x)
    {
           return Sll(sllsqrt(int2sll(x)));
    };
    
    inline static Sll sqrt(const float x)
    {
           return Sll(sllsqrt(dbl2sll((double)x)));
    };
    
    inline static Sll sqrt(const double x)
    {
           return Sll(sllsqrt(dbl2sll(x)));
    };


    inline static int ceil(const Sll& x)
    {
      int a=sll2int(x.sValue);
      if(x > a)
           return a+1;
      else
           return a;
    };

    inline static int ceil(const int x)
    {
      return x;
    };


    inline static int ceil(const float x)
    {
      int a=sll2int(dbl2sll((double)x));
      if(x > a)
           return a+1;
      else
           return a;
    };

    inline static int ceil(const double x)
    {
      int a=sll2int(dbl2sll(x));
      if(x > a)
           return a+1;
      else
           return a;
    };



    inline sll getValue()
    {
           return sValue;
    };
   
    /*Other class methods*/
    /*to be written*/
protected: 
    sll sValue;
    ull uValue;
};
